#include "songhandler.h"
#include "CDGWidget.h"

#include <qobject.h>
#include <qptrlist.h>
#include <qstringlist.h>
#include <iostream>
#include <qdir.h>
#include <qregexp.h>

#include <mythtv/mythcontext.h>
#include <mythtv/mythdbcon.h>
#include <mythtv/mythdialogs.h>
#include <mythtv/util.h>

#include "unzip.h"

using namespace std;


static QPtrList<SongHandler> *handlers = 0;
SongHandler* SongHandler::newInstance = 0;

static QString parseString = "%artist% - %title%";

    //First
    //%artist% - %title%

    //sc8429, 04 - Aqua - Barbiegirl
    //%text% - %artist% - %title%

    //EK19 - 01 The Bee Gees - You Win Again
    //%text% - %numbers% %artist% - %title%

    //15 - Mustang Sally - (Wilson Pickett) [012-The 70's Volume 1 Disk 2]
    //%numbers% - %ltext% - (%ltext%) %skip%



void SongHandler::registerHandler(SongHandler *handler)
{
    handlers->append(handler);
}

static void checkHandlers(void)
{
    // If a handlers list doesn't currently exist create one. Otherwise
    // clear the existing list so that we can regenerate a new one.
    if (! handlers)
       handlers = new QPtrList<SongHandler>;
    else
       handlers->clear();
    
    SongHandler::registerHandler(SongHandler::newHandler("cdg",
                                                         "cdg,zip", //Add .tar ?
                                                         "CdgSearchPathCDG"));
    SongHandler::registerHandler(SongHandler::newHandler("dvd",
                                                         "iso,bin",
                                                         "CdgSearchPathDVD"));
}

SongHandler* SongHandler::newHandler(QString name, QString extensions, QString pathsetting)
{
    newInstance = new SongHandler();
    newInstance->handlertype = name;
    newInstance->pathsetting = pathsetting;
    newInstance->validextensions = QStringList::split(",", extensions.stripWhiteSpace().remove(" "));
    cout << "Created handler: " << name << endl;
    return newInstance;
}


// Creates/rebuilds the handler list and then returns the count.
uint SongHandler::count(void)
{
    checkHandlers();
    return handlers->count();
}


// Recurse through the directory and gather a count on how many files there are to process.
// This is used for the progressbar info.
int SongHandler::buildFileCount(QString directory, SongHandler *handler)
{
    int filecount = 0;
    QDir curDir(directory);

				// If we can't read it's contents move on
    if (!curDir.isReadable()) 
        return 0;

    const QFileInfoList* List = curDir.entryInfoList();
    for (QFileInfoListIterator it(*List); it; ++it)
    {   
        QFileInfo Info(*it.current());
        QString RomName = Info.fileName();

        if (RomName == "." ||
            RomName == "..")
        {   
            continue;
        }

        if (Info.isDir())
        {   
            filecount += buildFileCount(Info.filePath(), handler);
            qApp->processEvents ();
            continue;
        }
        else
        {   
            if (handler->validextensions.count() > 0)
            {   
                QRegExp r;
                r.setPattern("^" + Info.extension( FALSE ) + "$");
                r.setCaseSensitive(false);
                QStringList result = handler->validextensions.grep(r);
                if (result.isEmpty()) {
                    continue;
                }
            }
            filecount++;

        }
    }

    return filecount;
}




SongHandler* SongHandler::GetHandler(SongInfo *songinfo)
{
    if (!songinfo)
       return NULL;


    SongHandler *handler = handlers->first();
    while(handler)
    {   
        if(songinfo->getType() == handler->SongType())
        {   
            return handler;
        }
        handler = handlers->next();
    }
    return handler;
}


void SongHandler::processSongs(SongHandler *handler)
{
    QString thequery;
    int maxcount = 0;
    MSqlQuery query(MSqlQuery::InitCon());

    QString path = gContext->GetSetting(handler->getSearchSetting());
    QDir d(path);
    
    if (d.exists())
    {
        MythBusyDialog *busy = new MythBusyDialog(QString("Counting %1(s)...")
                                                  .arg(handler->SongType().upper()));
        busy->start();
        maxcount = buildFileCount(path,handler);
        busy->Close();
        busy->deleteLater();
    }
    else 
    {
        cout << "Song-path does not exist : " << path << endl;
        return;
    }

    cout << "Amount of files: " << maxcount << endl;
    MythProgressDialog *dialog = new MythProgressDialog( QString("Scanning for %1(s)...")
                                                         .arg(handler->SongType().upper()), maxcount);
    dialog->setProgress(0);

    int filecount = 0;
    buildFileDB(path, handler, dialog, &filecount, parseString);

    dialog->Close();
    dialog->deleteLater();
}


void SongHandler::buildFileDB(QString directory, SongHandler *handler, 
                              MythProgressDialog *pdial, int* filecount,
                              QString parsestring)
{
    QDir curDir(directory);
                                // If we can't read it's contents move on
    if (!curDir.isReadable()) 
        return;
    

    // Check if new parsestring in directory    
    QDir::FilterSpec tmpFilter = curDir.filter();
    curDir.setFilter(QDir::Hidden | QDir::Files);

    const QFileInfoList * files = curDir.entryInfoList();
    for (QFileInfoListIterator it(*files); it; ++it)
    {
        QFileInfo fileinfo(*it.current());
        cout << "Check: " << fileinfo.fileName() << endl;
        if ( fileinfo.fileName() == ".cdgparse")
        {
            cout << ".cdgparse found in: " << directory << endl;
            QFile file( fileinfo.filePath() );
            if ( file.open( IO_ReadOnly ) ) {
                QTextStream stream( &file );
                
                if(!stream.atEnd()) {
                    QString line = stream.readLine();
                    if((line.find(QRegExp("%artist%"))>-1) && 
                       (line.find(QRegExp("%title%"))>-1))
                    {
                        cout << "Changing parsestring from " << parsestring << " to " << line << endl;
                        parsestring = line;
                    }
                }
                file.close();
            }
        }
    }
    curDir.setFilter(tmpFilter);



    curDir.setSorting( QDir:: DirsFirst | QDir::Name );
    const QFileInfoList* List = curDir.entryInfoList();
    for (QFileInfoListIterator it(*List); it; ++it)
    {
        QFileInfo Info(*it.current());
        QString FileName = Info.fileName();
        QString SongName = Info.baseName(TRUE);

        if (FileName == "." ||
            FileName == "..")
        {
            continue;
        }

        if (Info.isDir())
        {
            buildFileDB(Info.filePath(), handler, pdial, filecount, parsestring);
            continue;
        }
        else
        {

            // Is it correct type?
            if (handler->validextensions.count() > 0)
            {
                QRegExp r;

                r.setPattern("^" + Info.extension( FALSE ) + "$");
                r.setCaseSensitive(false);
                QStringList result = handler->validextensions.grep(r);
                if (result.isEmpty()) {
                    continue;
                }
            }

            SongInfo foundfile(SongName,Info.filePath());
            foundfile.setType(handler->SongType());

            if(Info.extension(FALSE).lower() == "zip")
            {
                cout << "Found zipfile: " << SongName << endl;

                findInZIP(foundfile, parsestring);
            }
            else
            {
                
                cout << "Found song : (" << handler->SongType() << " - " << Info.extension( FALSE ) <<
                    ") " << ": " << SongName << endl;

                if(Info.extension(FALSE).lower() == "cdg")
                {
                    foundfile.parseFilename(parsestring);
                }
                
                updateDB(foundfile);
            }            

            *filecount = *filecount + 1;
            pdial->setProgress(*filecount);
        }
    }
}


void SongHandler::updateDB(SongInfo& file)
{
    MSqlQuery query(MSqlQuery::InitCon());
    
    QString thequery;  
    thequery = QString("INSERT INTO cdgmetadata "
                       "(type, name, title, filepath, containerpath, hostname) VALUES "
                       "(\"%1\", \"%2\", \"%3\", \"%4\", \"%5\", \"%6\");")
        .arg(file.getType().latin1())
        .arg(file.getName().latin1())
        .arg(file.getTitle().latin1())
        .arg(file.getFilePath().latin1())
        .arg(file.getContainerPath().latin1())
        .arg(gContext->GetHostName());
    cout << "SQL: " << thequery << endl;
    query.exec(thequery);
}




//------------------------------------------
// Static used functions (config)
//------------------------------------------

void SongHandler::processAllSongs(void)
{
    checkHandlers();
    SongHandler *handler = handlers->first();

    while(handler)
    {
        handler->processSongs(handler);
        handler = handlers->next();
    }
}

void SongHandler::clearAllSongData(void)
{
    QStringList buttonText;
    buttonText += QObject::tr("No");
    buttonText += QObject::tr("Yes");

    int result = MythPopupBox::ShowButtonPopup(gContext->GetMainWindow(),
                               QObject::tr("Are you sure?"),
                               QString(QObject::tr("This will clear all Song Meta Data\n"
                                                   "from the database. Are you sure you\n"
                                                   "want to do this?" )),
                                                    buttonText, kDialogCodeButton0 );
    switch (result)
    {
        case 0:
            // Do Nothing
            break;
        case 1:
            MSqlQuery query(MSqlQuery::InitCon());
            QString thequery = QString("DELETE FROM cdgmetadata WHERE hostname = \"%1\"")
                .arg(gContext->GetHostName());
            query.exec(thequery);
            break;
    };
}



void SongHandler::findInZIP(SongInfo& foundfile, QString parsestring)
{
    unzFile zf;
    char filename_inzip[256];
    unz_file_info file_info;    
    int err;

    if ((zf = unzOpen(foundfile.getFilePath()) ))
    {
        int FoundFile;
        for (FoundFile = unzGoToFirstFile(zf);
             FoundFile == UNZ_OK;
             FoundFile = unzGoToNextFile(zf))
        {
            if (unzOpenCurrentFile(zf) == UNZ_OK)
            {
                err = unzGetCurrentFileInfo(zf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
                cout << "In zip: " << filename_inzip << endl;
                unzCloseCurrentFile(zf);

                QFileInfo Info( filename_inzip );
                if(Info.extension(FALSE).lower() == "cdg")
                {
                    foundfile.parseFilename(parsestring);
                    foundfile.setContainerPath(Info.filePath());
                    updateDB(foundfile);
                }               
            }
            else 
            {
                cout << "Cant open file." << endl;
            }
        }
        
        unzClose(zf);
    }
}

QString SongHandler::extractpackage(const SongInfo& info)
{
    unzFile zf;
    char filename_inzip[256];
    unz_file_info file_info;    
    QString tmpPath = "/tmp/";
    QString resultPath = "";

    if ((zf = unzOpen(info.getFilePath()) ))
    {
        QString cdgfile = info.getContainerPath();
        QString mp3file = cdgfile.left(cdgfile.findRev('.'));
        mp3file += ".mp3";

        if (unzLocateFile(zf,cdgfile,0) == UNZ_OK)
        {
            if (unzOpenCurrentFile(zf) == UNZ_OK)
            {
                int err = unzGetCurrentFileInfo(zf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
                cerr << "Found file: " << filename_inzip << " Err:" << err << endl;

                QByteArray buffer(file_info.uncompressed_size);
                unsigned int count = unzReadCurrentFile(zf, buffer.data(), file_info.uncompressed_size);
                if(count==file_info.uncompressed_size)
                {
                    cout << "Reading and writing cdgfile to: " << tmpPath << cdgfile << endl;

                    ofstream o;
                    o.open(tmpPath+cdgfile, ios::out | ios::binary);
                    o.write(buffer.data(),count);
                    o.close();
                    resultPath = tmpPath+cdgfile;
                }   
                unzCloseCurrentFile(zf);
            }
        }

        if (unzLocateFile(zf,mp3file,0) == UNZ_OK)
        {
            if (unzOpenCurrentFile(zf) == UNZ_OK)
            {
                int err = unzGetCurrentFileInfo(zf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
                cerr << "Found file: " << filename_inzip << " Err:" << err << endl;

                QByteArray buffer(file_info.uncompressed_size);
                unsigned int count = unzReadCurrentFile(zf, buffer.data(), file_info.uncompressed_size);
                if(count==file_info.uncompressed_size)
                {
                    cout << "Reading and writing mp3file to: " << tmpPath << mp3file << endl;

                    ofstream o;
                    o.open(tmpPath+mp3file, ios::out | ios::binary);
                    o.write(buffer.data(),count);
                    o.close();
                }   
                unzCloseCurrentFile(zf);
            }
        }
        unzClose(zf);
    }
    return resultPath;
}
